package com.shuijing.boot.transaction.service.impl;

import com.shuijing.boot.transaction.entity.User;
import com.shuijing.boot.transaction.service.TransactionService;
import com.shuijing.boot.transaction.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.util.concurrent.ThreadLocalRandom;

/**
 * @author 刘水镜
 * @blog https://liushuijinger.blog.csdn.net
 * @date 2021-05-04
 */
@Slf4j
@Service
public class TransactionServiceImpl implements TransactionService {

    @Autowired
    private UserService userService;

    @Override
    public void clean() {
        userService.remove(null);
    }

    private User getUserZhangSan() {
        User user = new User();
        user.setName("张三");
        int random = ThreadLocalRandom.current().nextInt(1000);
        String email = String.format("zhangsan%d@163.com", random);
        user.setEmail(email);
        user.setBirthDay(LocalDate.of(1986, 7, 11));
        return user;
    }

    private User getUserLiSi() {
        User user = new User();
        user.setName("李四");
        int random = ThreadLocalRandom.current().nextInt(1000);
        String email = String.format("lisi%d@163.com", random);
        user.setEmail(email);
        user.setBirthDay(LocalDate.of(1992, 10, 16));
        return user;
    }

    private User getUserWangWu() {
        User user = new User();
        user.setName("王五");
        int random = ThreadLocalRandom.current().nextInt(1000);
        String email = String.format("wangwu%d@163.com", random);
        user.setEmail(email);
        user.setBirthDay(LocalDate.of(1982, 3, 21));
        return user;
    }

    /**
     * 测试没有事务管理时，调用两个Required传播行为的新增方法并抛出外部异常。
     * 此情况下新增操作不会回滚，因为方法未启用事务管理。
     */
    @Override
    public void noTransaction_required_required_externalException() {
        User userZhang = getUserZhangSan();
        User userLi = getUserLiSi();
        userService.addWithRequired(userZhang);
        userService.addWithRequired(userLi);
        throw new RuntimeException("测试没有事务管理时，调用两个Required传播行为的新增方法并抛出外部异常");
    }

    /**
     * 测试没有事务管理时，调用两个Required传播行为的新增方法且第二个方法抛出异常。
     * 第一个新增操作不会回滚，因为没有事务上下文保障原子性。
     */
    @Override
    public void noTransaction_required_requiredException() {
        User userZhang = getUserZhangSan();
        User userLi = getUserLiSi();
        userService.addWithRequired(userZhang);
        userService.addWithRequiredAndException(userLi);
    }

    /**
     * 测试事务方法中连续调用两个Required传播行为新增操作，并抛出外部异常。
     * 整个事务上下文将被标记回滚，两个新增操作都会回滚。
     */
    @Override
    @Transactional
    public void transaction_required_required_externalException() {
        User userZhang = getUserZhangSan();
        User userLi = getUserLiSi();
        userService.addWithRequired(userZhang);
        userService.addWithRequired(userLi);
        throw new RuntimeException("测试事务方法中连续调用两个Required传播行为新增操作，并抛出外部异常");
    }

    /**
     * 测试事务方法中调用两个Required传播行为新增操作，第二个方法主动抛出异常。
     * 整个事务上下文将被标记回滚，两个新增操作都会回滚。
     */
    @Override
    @Transactional
    public void transaction_required_requiredException() {
        User userZhang = getUserZhangSan();
        User userLi = getUserLiSi();
        userService.addWithRequired(userZhang);
        userService.addWithRequiredAndException(userLi);
    }

    /**
     * 测试事务方法中捕获Required传播行为的异常。
     * 虽然捕获了异常，但由于事务已标记回滚，整个事务仍会回滚。
     */
    @Override
    @Transactional
    public void transaction_required_requiredException_try() {
        User userZhang = getUserZhangSan();
        User userLi = getUserLiSi();
        userService.addWithRequired(userZhang);
        try {
            userService.addWithRequiredAndException(userLi);
        } catch (Exception e) {
            log.error("发生异常，事务回滚！");
        }
    }

    /**
     * 测试事务方法中混合调用Required和RequiredNew传播行为并抛出外部异常。
     * 外部异常导致整个事务回滚，包含嵌套的独立新事务操作。
     */
    @Override
    @Transactional
    public void transaction_required_requiredNew_externalException() {
        User userZhang = getUserZhangSan();
        User userLi = getUserLiSi();
        userService.addWithRequired(userZhang);
        userService.addWithRequiredNew(userLi);
        throw new RuntimeException("测试事务方法中混合调用Required和RequiredNew传播行为并抛出外部异常");
    }

    /**
     * 测试事务方法中连续调用Required和两个RequiredNew传播行为并抛出异常。
     * 所有操作将在同一事务上下文中执行，最终全部回滚。
     */
    @Override
    @Transactional
    public void transaction_required_requiredNew_requiredNewException() {
        User userZhang = getUserZhangSan();
        User userLi = getUserLiSi();
        User userWang = getUserWangWu();
        userService.addWithRequired(userZhang);
        userService.addWithRequiredNew(userLi);
        userService.addWithRequiredNewAndException(userWang);
    }

    /**
     * 测试事务方法中捕获RequiredNew传播行为的异常。
     * 虽然捕获了嵌套事务的异常，但主事务仍会整体回滚。
     */
    @Override
    @Transactional
    public void transaction_required_requiredNewException_try() {
        User userZhang = getUserZhangSan();
        User userLi = getUserLiSi();
        User userWang = getUserWangWu();
        userService.addWithRequired(userZhang);
        userService.addWithRequiredNew(userLi);
        try {
            userService.addWithRequiredNewAndException(userWang);
        } catch (Exception e) {
            log.error("发生异常，事务回滚！");
        }
    }

    /**
     * 测试事务方法中连续调用两个Nested传播行为并抛出外部异常。
     * 所有嵌套事务操作将在同一事务上下文中执行并全部回滚。
     */
    @Override
    @Transactional
    public void transaction_nested_nested_externalException() {
        User userZhang = getUserZhangSan();
        User userLi = getUserLiSi();
        userService.addWithNested(userZhang);
        userService.addWithNested(userLi);
        throw new RuntimeException("测试事务方法中连续调用两个Nested传播行为并抛出外部异常");
    }

    /**
     * 测试事务方法中连续调用两个Nested传播行为且第二个抛出异常。
     * 嵌套事务操作将触发整个事务回滚。
     */
    @Override
    @Transactional
    public void transaction_nested_nestedException() {
        User userZhang = getUserZhangSan();
        User userLi = getUserLiSi();
        userService.addWithNested(userZhang);
        userService.addWithNestedAndException(userLi);
    }

    /**
     * 测试事务方法中混合调用Required和Nested传播行为并捕获异常。
     * 嵌套事务异常被捕获后，主事务仍会正常提交。
     */
    @Override
    @Transactional
    public void transaction_nested_nestedException_try() {
        User userZhang = getUserZhangSan();
        User userLi = getUserLiSi();
        User userWang = getUserWangWu();
        userService.addWithRequired(userZhang);
        userService.addWithNested(userLi);
        try {
            userService.addWithNestedAndException(userWang);
        } catch (Exception e) {
            log.error("发生异常，事务回滚！", e);
        }
    }
}
